package parser

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
)

// StatementType 定义SQL语句类型
type StatementType int

const (
	Unknown StatementType = iota
	CreateDatabase
	DropDatabase
	UseDatabase
	CreateTable
	DropTable
	CreateIndex
	CreateView
	CreateProcedure
	CreateFunction
	CreateTrigger
	Insert
	CreateSequence
	Delimiter
	Other
)

// SQLStatement 表示一个SQL语句
type SQLStatement struct {
	Type    StatementType
	Content string
	Line    int
}

// MySQLParser MySQL解析器
type MySQLParser struct {
	stats map[string]int
}

// internal parsing state for quote and comment tracking
type parseState struct {
	inSingleQuote  bool
	inDoubleQuote  bool
	inBlockComment bool
	delimiter      string
}

// splitTopLevelStatements 将可能包含多条SQL的字符串，按照顶层分号进行安全拆分。
// 仅在引号与块注释之外的分号位置进行拆分，避免误将字符串或注释中的分号当作语句结束。
// 该方法作为保险措施，用于处理某些边缘情况下未正确拆分的多语句行。
func splitTopLevelStatements(sql string) []string {
	var parts []string
	if sql = strings.TrimSpace(sql); sql == "" {
		return parts
	}

	inSingle := false
	inDouble := false
	inBlock := false
	start := 0
	i := 0
	for i < len(sql) {
		c := sql[i]
		next := byte(0)
		if i+1 < len(sql) {
			next = sql[i+1]
		}

		// 处理块注释起止
		if inBlock {
			if c == '*' && next == '/' {
				inBlock = false
				i += 2
				continue
			}
			i++
			continue
		}
		if !inSingle && !inDouble && c == '/' && next == '*' {
			inBlock = true
			i += 2
			continue
		}

		// 处理行注释：遇到 -- 或 #，跳过到行末
		if !inSingle && !inDouble {
			if c == '-' && next == '-' {
				// 跳至下一行（此函数不保留原始换行，视为普通字符流）
				// 找到下一个换行或字符串结尾
				j := i + 2
				for j < len(sql) && sql[j] != '\n' {
					j++
				}
				i = j
				continue
			}
			if c == '#' {
				j := i + 1
				for j < len(sql) && sql[j] != '\n' {
					j++
				}
				i = j
				continue
			}
		}

		// 处理引号与转义
		if c == '\'' && !inDouble {
			if inSingle {
				// 双写 '' 作为转义
				if next == '\'' {
					i += 2
					continue
				}
				// 反斜杠转义 \'
				if i > 0 && sql[i-1] == '\\' {
					i++
					continue
				}
				inSingle = false
			} else {
				inSingle = true
			}
			i++
			continue
		}
		if c == '"' && !inSingle {
			if inDouble {
				if i > 0 && sql[i-1] == '\\' {
					i++
					continue
				}
				inDouble = false
			} else {
				inDouble = true
			}
			i++
			continue
		}

		// 在顶层分号处拆分
		if !inSingle && !inDouble && !inBlock && c == ';' {
			part := strings.TrimSpace(sql[start : i+1])
			if part != "" {
				parts = append(parts, part)
			}
			start = i + 1
		}
		i++
	}

	// 余下内容（可能是最后一条未以分号结尾的语句）
	tail := strings.TrimSpace(sql[start:])
	if tail != "" {
		parts = append(parts, tail)
	}
	return parts
}

// SplitTopLevelStatements 对外导出：按照顶层分号安全拆分可能包含多语句的 SQL 字符串。
// 该函数仅在引号与块注释之外的分号位置进行拆分，避免误将字符串或注释中的分号当作语句结束。
// 注意：此方法不会解析语法结构，仅用于在转换后或其他阶段需要再次按语句边界切分的场景。
func SplitTopLevelStatements(sql string) []string {
	return splitTopLevelStatements(sql)
}

// ClassifyStatement 对外导出：识别单条 SQL 语句的类型
// 使用与解析器内部一致的规则对传入的 SQL 片段进行类型判断。
func ClassifyStatement(sql string) StatementType {
	p := NewMySQLParser()
	return p.classifyStatement(sql)
}

// processLine appends line content to currentStatement and emits complete statements
// whenever a semicolon is found outside of quotes/comments.
// It preserves original newlines and supports multiple statements per line.
func processLine(line string, state *parseState, currentStatement *strings.Builder, emit func()) {
	// ensure default delimiter
	if state.delimiter == "" {
		state.delimiter = ";"
	}

	// quick check for DELIMITER directive outside of quotes/comments
	// we do this line-wise to avoid mixing directive tokens into statements
	trimmed := strings.TrimSpace(line)
	upper := strings.ToUpper(trimmed)
	if !state.inSingleQuote && !state.inDoubleQuote && !state.inBlockComment && strings.HasPrefix(upper, "DELIMITER") {
		d := strings.TrimSpace(trimmed[len("DELIMITER"):])
		if d != "" {
			state.delimiter = d
		}
		// do not append this directive to the current statement buffer
		return
	}

	// iterate byte-wise; ASCII is sufficient for quote/comment detection
	i := 0
	for i < len(line) {
		c := line[i]
		// helper: peek next byte
		next := byte(0)
		if i+1 < len(line) {
			next = line[i+1]
		}

		// inside block comment: search for end */
		if state.inBlockComment {
			if c == '*' && next == '/' {
				state.inBlockComment = false
				currentStatement.WriteByte(c)
				currentStatement.WriteByte(next)
				i += 2
				continue
			}
			currentStatement.WriteByte(c)
			i++
			continue
		}

		// start of block comment (outside quotes)
		if !state.inSingleQuote && !state.inDoubleQuote && c == '/' && next == '*' {
			state.inBlockComment = true
			currentStatement.WriteByte(c)
			currentStatement.WriteByte(next)
			i += 2
			continue
		}

		// start of line comment '--' or '#': ignore rest of line (outside quotes)
		if !state.inSingleQuote && !state.inDoubleQuote {
			if c == '-' && next == '-' {
				// stop processing the rest of the line; do not add to statement
				break
			}
			if c == '#' {
				break
			}
		}

		// handle quotes and escapes
		if c == '\'' && !state.inDoubleQuote { // single quote context
			if state.inSingleQuote {
				// escaped single quote by doubling ''
				if next == '\'' {
					currentStatement.WriteByte(c)
					currentStatement.WriteByte(next)
					i += 2
					continue
				}
				// escaped single quote by backslash \'
				// lookback for previous char is backslash
				if i > 0 && line[i-1] == '\\' {
					currentStatement.WriteByte(c)
					i++
					continue
				}
				// end of single-quoted string
				state.inSingleQuote = false
				currentStatement.WriteByte(c)
				i++
				continue
			} else {
				// begin single-quoted string
				state.inSingleQuote = true
				currentStatement.WriteByte(c)
				i++
				continue
			}
		}

		if c == '"' && !state.inSingleQuote { // double quote context
			if state.inDoubleQuote {
				// escaped double quote by backslash \"
				if i > 0 && line[i-1] == '\\' {
					currentStatement.WriteByte(c)
					i++
					continue
				}
				// end of double-quoted string
				state.inDoubleQuote = false
				currentStatement.WriteByte(c)
				i++
				continue
			} else {
				// begin double-quoted string
				state.inDoubleQuote = true
				currentStatement.WriteByte(c)
				i++
				continue
			}
		}

		// statement terminator outside quotes/comments
		if !state.inSingleQuote && !state.inDoubleQuote && !state.inBlockComment {
			// default semicolon terminator
			if state.delimiter == ";" {
				if c == ';' {
					currentStatement.WriteByte(c)
					emit()
					currentStatement.Reset()
					i++
					continue
				}
			} else {
				// multi-char terminator (e.g., //, $$)
				dl := len(state.delimiter)
				if dl > 0 && i+dl <= len(line) {
					if line[i:i+dl] == state.delimiter {
						// do not include delimiter in emitted statement
						emit()
						currentStatement.Reset()
						i += dl
						continue
					}
				}
			}
		}

		// normal character
		currentStatement.WriteByte(c)
		i++
	}

	// preserve original newline at end of each processed line
	currentStatement.WriteByte('\n')
}

// NewMySQLParser 创建新的MySQL解析器
func NewMySQLParser() *MySQLParser {
	return &MySQLParser{
		stats: make(map[string]int),
	}
}

// Parse 解析SQL文件
func (p *MySQLParser) Parse(filePath string) ([]SQLStatement, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("无法打开文件: %v", err)
	}
	defer file.Close()

	var statements []SQLStatement
	scanner := bufio.NewScanner(file)
	// 增大Scanner缓冲区以支持超长行（如包含大量VALUES的INSERT语句）
	// 默认MaxScanTokenSize为64K，提升到10MB以避免因超长行导致扫描中断
	scanner.Buffer(make([]byte, 0, 1024*1024), 10*1024*1024)
	var currentStatement strings.Builder
	var state parseState
	lineNum := 0
	statementStartLine := 1

	for scanner.Scan() {
		lineNum++
		lineRaw := scanner.Text()

		// 使用精细化处理，确保只在引号/注释之外的分号处结束语句
		processLine(lineRaw, &state, &currentStatement, func() {
			sql := strings.TrimSpace(currentStatement.String())
			if sql == "" {
				return
			}
			// 保险拆分：处理偶发未被顶层分号正确拆分的多语句内容
			parts := splitTopLevelStatements(sql)
			if len(parts) == 0 {
				return
			}
			for _, part := range parts {
				stmtType := p.classifyStatement(part)
				statements = append(statements, SQLStatement{
					Type:    stmtType,
					Content: part,
					Line:    statementStartLine,
				})
				p.updateStats(stmtType)
			}
			// 下一条语句的起始行号为当前行（同一行多语句场景）
			statementStartLine = lineNum
		})
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("读取文件时出错: %v", err)
	}
	return statements, nil
}

// ParseStream 以流式方式解析SQL文件，按语句回调处理，避免一次性加载全部语句到内存
func (p *MySQLParser) ParseStream(filePath string, fn func(SQLStatement) error) error {
	file, err := os.Open(filePath)
	if err != nil {
		return fmt.Errorf("无法打开文件: %v", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	// 增大Scanner缓冲区以支持超长行（如包含大量VALUES的INSERT语句）
	scanner.Buffer(make([]byte, 0, 1024*1024), 10*1024*1024)
	var currentStatement strings.Builder
	var state parseState
	lineNum := 0
	statementStartLine := 1

	for scanner.Scan() {
		lineNum++
		lineRaw := scanner.Text()

		// 使用精细化处理，确保只在引号/注释之外的分号处结束语句
		processLine(lineRaw, &state, &currentStatement, func() {
			sql := strings.TrimSpace(currentStatement.String())
			if sql == "" {
				return
			}
			// 保险拆分：处理偶发未被顶层分号正确拆分的多语句内容
			parts := splitTopLevelStatements(sql)
			if len(parts) == 0 {
				return
			}
			for _, part := range parts {
				stmtType := p.classifyStatement(part)
				stmt := SQLStatement{
					Type:    stmtType,
					Content: part,
					Line:    statementStartLine,
				}
				p.updateStats(stmtType)
				if err := fn(stmt); err != nil {
					return
				}
			}
			// 下一条语句的起始行号为当前行（同一行多语句场景）
			statementStartLine = lineNum
		})
	}

	if err := scanner.Err(); err != nil {
		return fmt.Errorf("读取文件时出错: %v", err)
	}
	return nil
}

// classifyStatement 分类SQL语句
func (p *MySQLParser) classifyStatement(sql string) StatementType {
	sql = strings.ToUpper(strings.TrimSpace(sql))

	// 空语句或注释
	if sql == "" || strings.HasPrefix(sql, "--") || strings.HasPrefix(sql, "/*") {
		return Unknown
	}

	// 使用正则表达式匹配不同类型的语句
	// 允许可选的 MySQL DEFINER 子句：CREATE DEFINER=... PROCEDURE/FUNCTION/TRIGGER
	definerOpt := `(?:DEFINER\s*=\s*\S+\s*)?`
	patterns := map[StatementType]*regexp.Regexp{
		CreateDatabase: regexp.MustCompile(`^CREATE\s+DATABASE`),
		DropDatabase:   regexp.MustCompile(`^DROP\s+DATABASE`),
		UseDatabase:    regexp.MustCompile(`^USE\s+`),
		CreateTable:    regexp.MustCompile(`^CREATE\s+TABLE`),
		DropTable:      regexp.MustCompile(`^DROP\s+TABLE`),
		CreateIndex:    regexp.MustCompile(`^CREATE\s+(UNIQUE\s+)?INDEX`),
		// 支持 CREATE [OR REPLACE] [ALGORITHM=..] [DEFINER=..] [SQL SECURITY ..] VIEW
		CreateView:      regexp.MustCompile(`^CREATE\s+(?:OR\s+REPLACE\s+)?(?:ALGORITHM\s*=\s*\w+\s*)?(?:DEFINER\s*=\s*\S+\s*)?(?:SQL\s+SECURITY\s+\w+\s*)?VIEW`),
		CreateProcedure: regexp.MustCompile(`^CREATE\s+` + definerOpt + `PROCEDURE`),
		CreateFunction:  regexp.MustCompile(`^CREATE\s+` + definerOpt + `FUNCTION`),
		CreateTrigger:   regexp.MustCompile(`^CREATE\s+` + definerOpt + `TRIGGER`),
		Insert:          regexp.MustCompile(`^INSERT\s+INTO`),
		CreateSequence:  regexp.MustCompile(`^CREATE\s+SEQUENCE`),
		Delimiter:       regexp.MustCompile(`^DELIMITER`),
	}

	for stmtType, pattern := range patterns {
		if pattern.MatchString(sql) {
			return stmtType
		}
	}

	// 对于其他有效的SQL语句（如UPDATE, DELETE, ALTER等），返回Other
	return Other
}

// updateStats 更新统计信息
func (p *MySQLParser) updateStats(stmtType StatementType) {
	switch stmtType {
	case CreateDatabase:
		p.stats["数据库"]++
	case CreateTable:
		p.stats["表"]++
	case CreateIndex:
		p.stats["索引"]++
	case CreateView:
		p.stats["视图"]++
	case CreateProcedure:
		p.stats["存储过程"]++
	case CreateFunction:
		p.stats["函数"]++
	case CreateTrigger:
		p.stats["触发器"]++
	case Insert:
		p.stats["插入语句"]++
	case CreateSequence:
		p.stats["序列"]++
	case Other:
		p.stats["其他语句"]++
	case Unknown:
		p.stats["未知语句"]++
	}
	p.stats["总语句数"]++
}

// GetStats 获取统计信息
func (p *MySQLParser) GetStats() map[string]int {
	return p.stats
}

// GetStatementTypeName 获取语句类型名称
func GetStatementTypeName(stmtType StatementType) string {
	names := map[StatementType]string{
		CreateDatabase:  "CREATE_DATABASE",
		DropDatabase:    "DROP_DATABASE",
		UseDatabase:     "USE",
		CreateTable:     "CREATE_TABLE",
		DropTable:       "DROP_TABLE",
		CreateIndex:     "CREATE_INDEX",
		CreateView:      "CREATE_VIEW",
		CreateProcedure: "CREATE_PROCEDURE",
		CreateFunction:  "CREATE_FUNCTION",
		CreateTrigger:   "CREATE_TRIGGER",
		Insert:          "INSERT",
		CreateSequence:  "CREATE_SEQUENCE",
		Delimiter:       "DELIMITER",
		Other:           "OTHER",
		Unknown:         "UNKNOWN",
	}
	return names[stmtType]
}
